<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="description" content="常见分布式事务解决方案

两阶段提交(2PC,Two-phase Commit)
TCC补偿模式
基于本地消息表实现最终一致性
最大努力通知
基于可靠消息最终一致性方案

1、两阶段提交(2PC)
两阶段提交又称2PC,2PC是一个非常经典..." />
    <meta name="keywords" content="事务,分布式事务" />
    <link rel="stylesheet" href="https://hzqyihui.github.io/media/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://hzqyihui.github.io/styles/main.css">
    <!-- 代码渲染风格css -->
    
    <link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/styles/rainbow.min.css">
    
    <!-- 代码高亮插件 -->
    <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/highlight.min.js"></script>
    <!-- 代码复制插件 -->
    <script src="https://cdn.jsdelivr.net/gh/850552586/ericamcdn@0.1/js/clipboard.min.js"></script>
    <!-- live2d插件 -->
    <link rel="stylesheet" href="https://hzqyihui.github.io/media/css/live2d.css">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/5.13.1/css/all.min.css" rel="stylesheet">
    <!-- 数学公式 -->
    
    <title>常见分布式事务解决方案：</title>

    <!-- 大纲索引css调整 -->
    
    <style>
        .markdownIt-TOC {
            margin-left: 28%;
            padding-left: 2px;
            width: 80%;
        }

        .markdownIt-TOC li {
            padding-left: 8%;
        }
    </style>
    
</head>

<body>
    <div id="landlord">
        <div class="message" style="opacity:0"></div>
        <canvas id="live2d" width="240" height="250" class="live2d"></canvas>
    </div>
    <div id="domainname" style="display:none">https://hzqyihui.github.io</div>
    <div id="content">
        <div>
            <!----------- 移动端导航栏👇 ----------------->
            <div id="bar">
                <style>
  .navmobile {
    z-index: 15;
    max-height: 40vh;
    overflow-y: scroll;
    overflow-x: hidden;
  }

  @media screen and (max-width: 1200px) {
    .navbar {
      max-width: 100vw;
    }
  }

  @media screen and (max-width:1200px) and (min-width: 768px) {
    @media (min-width: 768px) {
      .navbar-nav {
        float: right;
        margin: 0;
      }
    }
  }

  @media screen and (max-width:768px) {
    .mbapspan {
      padding: 0;
    }
  }
</style>
<div class="pjaxloading"  onclick="pjaxloadingClose()">
  <div class="loadingbackground"></div>
  <div id="loading">
    <div class="loading">
    </div>
  </div>
</div>
<nav class="navbar navbar-inverse navbar-fixed-top navmobile">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#barmenu"
        aria-expanded="false" id="barbutton">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a data-pjax class="navbar-brand" style="color:white" href="https://hzqyihui.github.io">MaxHua&#39;s Note&nbsp;&nbsp;|</a>
      <span class="navbar-brand mbapspan"><i id="mbaplayer" class="fa fa-play-circle-o" aria-hidden="true"
          style="font-size: 28px;top: 12px;position: absolute;" onclick="aplayerPlay();"></i></span>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="barmenu">
      <ul class="nav navbar-nav">
        
        
        <li>
          <a data-pjax href="/" style="color:white">
            首页
          </a>
        </li>
        
        
        
        <li>
          <a data-pjax href="/archives" style="color:white">
            归档
          </a>
        </li>
        
        
        
        <li>
          <a data-pjax href="/tags" style="color:white">
            标签
          </a>
        </li>
        
        
        
        <li>
          <a data-pjax href="/post/about" style="color:white">
            关于
          </a>
        </li>
        
        
        
        <li><a data-pjax href="https://hzqyihui.github.io/talk" style="color:white">说说</a></li>
        
        
          
        <li><a data-pjax href="https://hzqyihui.github.io/friends" style="color:white">友链</a></li>

        
        </ul>
      </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
  </nav>


  <!-- 引入jQuery核心js文件 -->
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
  <script>
    let btstate = false;
    var bt = $("#barbutton");
    var bm = $("#barmenu");
    bt.click(function () {
      dropdown();
    })
    function dropdown() {
      console.log(btstate);
      //下拉
      if (btstate == false) {
        bt.removeClass("collapsed");
        bt.attr("aria-expanded", "true");
        bm.attr("aria-expanded", "true")
        bm.fadeIn(700);
        btstate = true;
      }
      else {
        bt.addClass("collapsed");
        bt.attr("aria-expanded", "false");
        bm.removeClass("in");
        bm.hide();
        bm.attr("aria-expanded", "false");
        btstate = false;
      }
    }
  </script> 
            </div>
            <!----------- 移动端导航栏👆 ----------------->

            <!--------------- pc端菜单栏👇 ---------------->
            <div class="side"><head>
    <link rel="stylesheet" href="https://hzqyihui.github.io/media/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://hzqyihui.github.io/styles/main.css">
    
    <link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/styles/rainbow.min.css">
    
    <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/highlight.min.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/850552586/ericamcdn@0.1/js/clipboard.min.js"></script>
    <link rel="stylesheet" href="https://hzqyihui.github.io/media/css/live2d.css">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/5.13.1/css/all.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- fancybox -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" />
    <script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>


    <!--------- 数学公式👇---- -->
    
    <!--------- 数学公式👆----------------- -->
    <!--------- daovocie功能👇 ------------------->
    
    <script type="text/javascript">
        window.$crisp = [];
        window.CRISP_WEBSITE_ID = "6fbd6157-accf-49b0-9398-56a347b943f8";
        (function () {
            d = document;
            s = d.createElement("script");
            s.src = "https://client.crisp.chat/l.js";
            s.async = 1;
            d.getElementsByTagName("head")[0].appendChild(s);
        })();
    </script>
    
    <!--------- daovocie功能👆 ------------------->
    <!-- -----------------twikoo评论系统引入👇 --------->
    <script src="https://cdn.jsdelivr.net/npm/twikoo@1.3.1/dist/twikoo.all.min.js"></script>
    <!-- -----------------twikoo评论系统引入👆 --------->
    <!---------------- leancloud api 功能依赖引入👇 -------->
    <script src="https://cdn.jsdelivr.net/npm/leancloud-storage@3.13.0/dist/av-min.js"></script>
    <!---------------- leancloud api 功能依赖引入👆 -------->
    <!------------------ valine评论系统引入👇 ------------------>
    <script src="https://hzqyihui.github.io/media/js/valine.min.js"></script>
    <!------------------ valine评论系统引入👆 ------------------>
    <!---------------- 今日诗词api功能依赖👇 ----------------------->
    <script src="https://sdk.jinrishici.com/v2/browser/jinrishici.js" charset="utf-8"></script>
    <!---------------- 今日诗词api功能依赖👆 ----------------------->
    <!---------------- 今日诗词api功能依赖👇 ----------------------->
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/ispeak-bber/ispeak-bber.min.js" charset="utf-8"></script>
    <!---------------- 哔哔功能依赖👆 ----------------------->


</head>

<style>
    body {
        overflow-x: hidden;
        height: 100vh;
        overflow-y: scroll;
        cursor: url("https://hzqyihui.github.io/media/images/mouse_cursor.png"), auto
    }
</style>

<body>
    <!--=------ 页面加载动态画面👇 --------------->
    <div class="pjaxloading" onclick="pjaxloadingClose()">
        <div class="loadingbackground"></div>
        <div id="loading-aname">
            <div class="loading-aname">
            </div>
        </div>
    </div>
    <!--=------ 页面加载动态画面👆 --------------->

    <!--------------- 左侧菜单栏👇 ------------------------>
    
    <div id=side>
        <div class="r_m_div">
            <a onclick="showAaplayer()"><i style="font-size: 20px;"><img
                        src='https://hzqyihui.github.io/media/images/music.png' alt="音乐播放器" class="musicbtn"></i></a>
        </div>
        <div class="avatar-border">
            <img src="https://hzqyihui.github.io/images/avatar.png?v=1665303694187" class="avatar">
        </div>
        <div class="sitename">MaxHua&#39;s Note</div>
        <span class="description" data-text='["当能力和欲望不匹配时，人会极度痛苦。"]'>&nbsp;</span>
        <div class="siteinfocontainer">
            <span class="siteinfo">文章</span>
            <span class="siteinfo">标签</span>
        </div>
        <div class="siteinfocontainer">
            <span class="siteinfo" id="siteinfo-postsnum"></span>
            <span class="siteinfo">60</span>
        </div>
        
        <div class="search">
            <!-- <input type="text" class="search-input" placeholder="标题搜索(●'◡'●)" /> -->
            <input type="text" class="search-input" placeholder="标题搜索 ⚆_⚆ つ♡">
            <div class="search-results"></div>
        </div>
        
    <div class="share-button">
        <span>Look At Me</span>
        
        <a href="https://github.com/hzqyihui" target="_blank"><i style="font-size: 20px;"><img class="icon"
                    src="https://hzqyihui.github.io/media/images/github.png" alt=""></i></a>
        
        
        <a onclick="showqq()"><i style="font-size: 20px;"><img class="icon"
                    src="https://hzqyihui.github.io/media/images/QQ.png" alt=""></i></a>
        
        
        <a href="https://zhihu.com/people/hzq_yihui" target="_blank"><i style="font-size: 20px;"><img class="icon"
                    src="https://hzqyihui.github.io/media/images/zh.png" alt=""></i></a>
        
        
        
        
        
        <a href="https://music.163.com/#/user/home?id=1318641230" target="_blank"><i style="font-size: 20px;"><img class="icon"
                    src="https://hzqyihui.github.io/media/images/wyy.png" alt=""></i></a>
        
        
    </div>
    <div id="qq" style="display:none">77430203</div>
    <!------------ 加载菜单按钮👇 ------------------------->
    <div class="menucontainer">
        
        
        <div class="mchocie description">
            <a data-pjax href="/" class="menubutton" style="margin-right: 15px;">
                <i><img class="icon menuicon" src="" alt="首页"></i>&nbsp;<span
                    data-text="首页">首页</span>
            </a>
        </div>
        
        
        
        <div class="mchocie description">
            <a data-pjax href="/archives" class="menubutton" style="margin-right: 15px;">
                <i><img class="icon menuicon" src="" alt="归档"></i>&nbsp;<span
                    data-text="归档">归档</span>
            </a>
        </div>
        
        
        
        <div class="mchocie description">
            <a data-pjax href="/tags" class="menubutton" style="margin-right: 15px;">
                <i><img class="icon menuicon" src="" alt="标签"></i>&nbsp;<span
                    data-text="标签">标签</span>
            </a>
        </div>
        
        
        
        <div class="mchocie description">
            <a data-pjax href="/post/about" class="menubutton" style="margin-right: 15px;">
                <i><img class="icon menuicon" src="" alt="关于"></i>&nbsp;<span
                    data-text="关于">关于</span>
            </a>
        </div>
        
        

        
        <div class="mchocie description">
            <a data-pjax href="https://hzqyihui.github.io/talk" class="menubutton" style="margin-right: 15px;">
                <i><img class="icon menuicon" src="" alt="说说"></i>&nbsp;<span data-text="说说">说说</span>
            </a>
        </div>
        
        
        
        <div class="mchocie description">
            <a data-pjax href="https://hzqyihui.github.io/friends" class="menubutton" style="margin-right: 15px;">
                <i><img class="icon menuicon" src="" alt="友人帐"></i>&nbsp;<span data-text="友人帐">友人帐</span>
            </a>
        </div>
        
        <!--  -->
    </div>
    <!------------ 加载菜单按钮👆 ------------------------->
    <hr>
    <style>
    .copyright {
        font-size: 15px;
        color: #d9d9d9;
    }


    @media screen and (min-width: 1200px) {
        .foot {
            width: 100%;
            height: 130px;
        }
    }

    @media screen and (max-width: 1200px) {
        .foot {
            display: none;
        }
    }
</style>

<body>
    <div class="foot">
        <div id="footinfo">Powered by <a href="https://github.com/hzqyihui/" target="_blank">Maxhua</a> | Theme: <a
                href="https://github.com/850552586/gridea-theme-fog">Fog</a>
            <div>
                
                <div id="sitegotimeDate">载入天数...</div>
                <div id="sitegotimes">载入时分秒...</div>
                <div id="cussitetime" style="display:none">02/22/2018</div>
                
            </div>
            <div>
                
            </div>
            <div class="copyright" style="color:white;text-align: center;">
            </div>
        </div>



    </div>
</body>

<script>
    //----------------------站点运行时间
    var now = new Date();

    function createtime() {
        var sitegotime = document.getElementById("cussitetime").innerHTML + " 00:00:00";
        var grt = new Date(sitegotime); //此处修改你的建站时间或者网站上线时间 
        now.setTime(now.getTime() + 250);
        days = (now - grt) / 1000 / 60 / 60 / 24;
        dnum = Math.floor(days);
        hours = (now - grt) / 1000 / 60 / 60 - (24 * dnum);
        hnum = Math.floor(hours);
        if (String(hnum).length == 1) {
            hnum = "0" + hnum;
        }
        minutes = (now - grt) / 1000 / 60 - (24 * 60 * dnum) - (60 * hnum);
        mnum = Math.floor(minutes);
        if (String(mnum).length == 1) {
            mnum = "0" + mnum;
        }
        seconds = (now - grt) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
        snum = Math.round(seconds);
        if (String(snum).length == 1) {
            snum = "0" + snum;
        }
        document.getElementById("sitegotimeDate").innerHTML = "本站已安全运行 " + dnum + " 天 ";
        document.getElementById("sitegotimes").innerHTML = hnum + " 小时 " + mnum + " 分 " + snum + " 秒";
    }

    let intervalId = setInterval("createtime()", 250);
</script>

    </div>

    <img class="moon" src="https://hzqyihui.github.io/media/images/moon.png" onclick="moonlight()">
    <script src="https://hzqyihui.github.io/media/js/wordshow.js"></script>
    
    <!--------------- 左侧菜单栏👆 ------------------------>

    <!----------------- 顶部菜单栏👇 ------------------------>
    
    <!----------------- 顶部菜单栏👆 ------------------------>

<!--核心功能加载-->
<script src="https://hzqyihui.github.io/media/js/mainfunc.js"></script>

<!------------- Fog1.0版本彩蛋👇 -------------->
    <!-- <section id="terrestrial" class="popup">
        <a href="#" class="back">&lt; back</a>
    </section>

    <div class="flycontainer">
        <div class="flyword">御风飞行中...</div>
    </div>

    <script>
        $("#fog_go").click(function(){fog_go()});
        async function fog_go(){
            $(".bars").hide()
            $("#live2d").hide()
            await sleep(3000);
            $(".flycontainer").css("display","flex");
            $(".popup").fadeOut(500)
            await sleep(2000);
            var sitename = "MaxHua&#39;s Note";
            getFogUser(sitename);
        }

        function fog_goregis(){
            var sitename = "MaxHua&#39;s Note";
            var siteurl = "https://hzqyihui.github.io";
            regisFogUser(sitename,siteurl);
        }
        fog_goregis();
    </script> -->
<!------------- Fog1.0版本彩蛋👆 -------------->
<script>
    function change_topmenu(){
        tmcolor = "black"
        tsnamecolor = "antiquewhite"
        $(".navbar-inverse .navbar-nav>li>a").css("color",tmcolor)
        $(".navbar-inverse .navbar-brand").css("color",tsnamecolor)
        $(".navbar-fixed-top").css("border-radius","0 0 10px 10px")
    }
</script>

<!------ 根据自定义配置声明变量👇 --------------------->
    
    <script>
        var postnumChoice = true
    </script>
    
    
    <script>
        var shareChoice = true
    </script>
    
    
    <script>
        var donateChoice = true
    </script>
    
    
    <script>
        var searchChoice = true
    </script>
    
    <script>let postsnum = 0;</script>
    <div id="side-posttitle" style="display: none;">
    
    
    Go语言Sync包的锁
    <script>
        postsnum += 1
    </script>
    
    
    
    
    
    Nacos安装和配置：
    <script>
        postsnum += 1
    </script>
    
    
    
    Nacos基本使用：
    <script>
        postsnum += 1
    </script>
    
    
    
    布隆过滤器
    <script>
        postsnum += 1
    </script>
    
    
    
    brew换源：
    <script>
        postsnum += 1
    </script>
    
    
    
    jenkins的安装和配置：
    <script>
        postsnum += 1
    </script>
    
    
    
    Kong怎么和Consul配套使用？
    <script>
        postsnum += 1
    </script>
    
    
    
    Kong的安装和配置：
    <script>
        postsnum += 1
    </script>
    
    
    
    什么是API网关？
    <script>
        postsnum += 1
    </script>
    
    
    
    RocketMQ Go 消息使用：
    <script>
        postsnum += 1
    </script>
    
    
    
    服务雪崩
    <script>
        postsnum += 1
    </script>
    
    
    
    gRPC的超时和重试
    <script>
        postsnum += 1
    </script>
    
    
    
    常见的幂等性解决方案
    <script>
        postsnum += 1
    </script>
    
    
    
    Rocket基本概念：
    <script>
        postsnum += 1
    </script>
    
    
    
    Rocket安装和配置
    <script>
        postsnum += 1
    </script>
    
    
    
    常见分布式事务解决方案：
    <script>
        postsnum += 1
    </script>
    
    
    
    CAP和BASE理论：
    <script>
        postsnum += 1
    </script>
    
    
    
    事务和分布式事务：
    <script>
        postsnum += 1
    </script>
    
    
    
    ElasticSearch学习：
    <script>
        postsnum += 1
    </script>
    
    
    
    什么是elasticsearch
    <script>
        postsnum += 1
    </script>
    
    
    
    Redis 的 RedLock：
    <script>
        postsnum += 1
    </script>
    
    
    
    常见的分布式锁的实现方案：
    <script>
        postsnum += 1
    </script>
    
    
    
    单机锁
    <script>
        postsnum += 1
    </script>
    
    
    
    Go工具-随机获取可用端口并监听：
    <script>
        postsnum += 1
    </script>
    
    
    
    负载均衡算法：
    <script>
        postsnum += 1
    </script>
    
    
    
    负载均衡策略：
    <script>
        postsnum += 1
    </script>
    
    
    
    JWT集成在Gin框架中
    <script>
        postsnum += 1
    </script>
    
    
    
    Go工具-Viper配置文件管理器
    <script>
        postsnum += 1
    </script>
    
    
    
    二叉树层序遍历
    <script>
        postsnum += 1
    </script>
    
    
    
    二叉树后序遍历
    <script>
        postsnum += 1
    </script>
    
    
    
    二叉树中序遍历
    <script>
        postsnum += 1
    </script>
    
    
    
    二叉树先序遍历
    <script>
        postsnum += 1
    </script>
    
    
    
    Dockerfile 的语法使用：
    <script>
        postsnum += 1
    </script>
    
    
    
    Nginx重写和转发规则：
    <script>
        postsnum += 1
    </script>
    
    
    
    HTTP版本：
    <script>
        postsnum += 1
    </script>
    
    
    
    HTTP请求响应过程：
    <script>
        postsnum += 1
    </script>
    
    
    
    TCP和UDP的区别：
    <script>
        postsnum += 1
    </script>
    
    
    
    插入排序
    <script>
        postsnum += 1
    </script>
    
    
    
    Go工具-proto文件编译
    <script>
        postsnum += 1
    </script>
    
    
    
    二分查找算法
    <script>
        postsnum += 1
    </script>
    
    
    
    sed命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    xargs命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    firewall命令（防火墙）：
    <script>
        postsnum += 1
    </script>
    
    
    
    nohup 命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    安装 FFmpeg：
    <script>
        postsnum += 1
    </script>
    
    
    
    umask命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    shell 笔记：
    <script>
        postsnum += 1
    </script>
    
    
    
    GIT分支与工作流
    <script>
        postsnum += 1
    </script>
    
    
    
    netstat命令：查看端口占用
    <script>
        postsnum += 1
    </script>
    
    
    
    nl命令：带行号查看文件
    <script>
        postsnum += 1
    </script>
    
    
    
    Git奇难杂症：
    <script>
        postsnum += 1
    </script>
    
    
    
    Redis安装笔记
    <script>
        postsnum += 1
    </script>
    
    
    
    排序的稳定性含义：
    <script>
        postsnum += 1
    </script>
    
    
    
    希尔排序
    <script>
        postsnum += 1
    </script>
    
    
    
    SSH开启无密码登录
    <script>
        postsnum += 1
    </script>
    
    
    
    改进版快速排序
    <script>
        postsnum += 1
    </script>
    
    
    
    冒泡排序
    <script>
        postsnum += 1
    </script>
    
    
    
    堆排序
    <script>
        postsnum += 1
    </script>
    
    
    
    选择排序
    <script>
        postsnum += 1
    </script>
    
    
    
    归并排序
    <script>
        postsnum += 1
    </script>
    
    
    
    位运算计算中间值：
    <script>
        postsnum += 1
    </script>
    
    
    
    递归的时间复杂度：
    <script>
        postsnum += 1
    </script>
    
    
    
    对数器
    <script>
        postsnum += 1
    </script>
    
    
    
    stat命令： 查看文件的修改
    <script>
        postsnum += 1
    </script>
    
    
    
    Windows快捷键
    <script>
        postsnum += 1
    </script>
    
    
    
    各类查找命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    Go进阶知识点
    <script>
        postsnum += 1
    </script>
    
    
    
    date命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    find 命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    Linux进程，内存查看
    <script>
        postsnum += 1
    </script>
    
    
    
    Go基础知识点
    <script>
        postsnum += 1
    </script>
    
    
    
    tree命令
    <script>
        postsnum += 1
    </script>
    
    
    
    GOROOT, GOPATH
    <script>
        postsnum += 1
    </script>
    
    
    
    locate 命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    Nginx 安装
    <script>
        postsnum += 1
    </script>
    
    
    
    PHP 知识点分享
    <script>
        postsnum += 1
    </script>
    
    
    
    Zsh 插件安装：
    <script>
        postsnum += 1
    </script>
    
    
    
    Linux系统基本知识点
    <script>
        postsnum += 1
    </script>
    
    
    
    Linux操作提升效率
    <script>
        postsnum += 1
    </script>
    
    
    
    Linux命令操作快捷键
    <script>
        postsnum += 1
    </script>
    
    
    
    YAML语法
    <script>
        postsnum += 1
    </script>
    
    
    
    VIM学习笔记：
    <script>
        postsnum += 1
    </script>
    
    
    
    经典快速排序
    <script>
        postsnum += 1
    </script>
    
    
    
    crontab
    <script>
        postsnum += 1
    </script>
    
    
    
    环境变量etc profile文件解析：
    <script>
        postsnum += 1
    </script>
    
    
    
    Linux etc sudoers文件说明：
    <script>
        postsnum += 1
    </script>
    
    
    
    Linux 用户，用户组相关命令
    <script>
        postsnum += 1
    </script>
    
    
    
    ln命令：对文件做链接，类似Windows的快捷方式
    <script>
        postsnum += 1
    </script>
    
    
    
    df
    <script>
        postsnum += 1
    </script>
    
    
    
    chmod：
    <script>
        postsnum += 1
    </script>
    
    
    
    用户埋点处理，用户行为分析技术：
    <script>
        postsnum += 1
    </script>
    
    
    
    PHP SPL扩展库
    <script>
        postsnum += 1
    </script>
    
    
    
    Beanstalked学习笔记：
    <script>
        postsnum += 1
    </script>
    
    
    
    tar命令：
    <script>
        postsnum += 1
    </script>
    
    
    
    GIT 原理   ：
    <script>
        postsnum += 1
    </script>
    
    
    
    tmux使用
    <script>
        postsnum += 1
    </script>
    
    
    
    Debian9学习笔记：
    <script>
        postsnum += 1
    </script>
    
    
    
    Docker学习笔记
    <script>
        postsnum += 1
    </script>
    
    
    
    在Debian9安装Docker
    <script>
        postsnum += 1
    </script>
    
    
    
    Git学习笔记
    <script>
        postsnum += 1
    </script>
    
    
    
    
    </div>
    
        <script>
            var menupos = 'left';
            $("#siteinfo-postsnum").html(postsnum);
        </script>
    
    <script>
        let commentsHide = false
    </script>
    <!------ 根据自定义配置声明变量👆 --------------------->


    <!----------根据自定义配置设置图标信息👇---------->
    <script>
        var icondict = new Array();
    </script>
    
        
            <script>
                var iconname = "首页";
                var iconurl = "Home";
                icondict[iconname] = iconurl;
            </script>
        
            <script>
                var iconname = "关于";
                var iconurl = "About";
                icondict[iconname] = iconurl;
            </script>
        
            <script>
                var iconname = "友人帐";
                var iconurl = "Friends";
                icondict[iconname] = iconurl;
            </script>
        
            <script>
                var iconname = "归档";
                var iconurl = "Archive";
                icondict[iconname] = iconurl;
            </script>
        
            <script>
                var iconname = "标签";
                var iconurl = "Tag";
                icondict[iconname] = iconurl;
            </script>
        
            <script>
                var iconname = "说说";
                var iconurl = "Talk";
                icondict[iconname] = iconurl;
            </script>
        
    
    <!----------根据自定义配置设置图标信息👆---------->
</body>

<script src="https://hzqyihui.github.io/media/share/dist/Share.js"></script>
<!-- 页面pjax测试 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.pjax/2.0.1/jquery.pjax.js"></script>
<!--确保jQuery已经在此之前加载-->
<!-- <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script> -->
<script src="https://hzqyihui.github.io/media/js/all-pjax.js"></script>
<script>
    //-------------------------------说说功能👇--------------------------------------
    let talksall = [];
    let talks = [];
    let talkindex = 0;
    get();

    function get() {
        var t = JSON.parse('[]');
        talkindex = t.length - 1;
        for (var i = 0; i <= t.length - 1; i++) {
            var d = {};
            d["time"] = t[i].time;
            d["author"] = t[i].author;
            d["text"] = t[i].text;
            d["img"] = t[i].img;
            talksall.push(d);
        }
    }

    function add() {
        for (var i = talkindex; i >= 0 && i > talkindex - 3; i--) {
            talks.push(talksall[i]);
        }
        talkindex = (talkindex - 3) >= 0 ? (talkindex - 3) : -1;
        if (talkindex < 0) $("#getmorebt").hide();
        show();
    }

    function show() {
        var talkT = document.getElementById("talkContainer");
        talkT.innerHTML = "";
        for (var i = 0; i < talks.length; i++) {
            if (talks[i].img != '') {
                talkT.innerHTML += '\
            <div class="talk" v-for="t in talks"> \
                <span data-fancybox="images" href="' + talks[i].img + '"><img src="' + talks[i].img + '" class="talkimg"></span> \
                <div class="ainfo">\
                    <div class="ainfo-intro">\
                        <div class="author">' + talks[i].author + '</div>·\
                        <div class="time">' + talks[i].time + '</div>\
                    </div>\
                </div>\
                <div class="talkcontent">' + talks[i].text + '\
                </div\>\
            ';
            } else {
                talkT.innerHTML += '\
            <div class="talk" v-for="t in talks"> \
                <div class="ainfo">\
                    <div class="ainfo-intro">\
                        <div class="author">' + talks[i].author + '</div>·\
                        <div class="time">' + talks[i].time + '</div>\
                    </div>\
                </div>\
                <div class="talkcontent">' + talks[i].text + '\
                </div\>\
            ';
            }
        }
    }
    //-------------------------------说说功能👆--------------------------------------
</script>

<!---->
<script>
    function replaceAvatar() {
        twikoo_avatar = $(".tk-avatar-img");
        for (var i = 0; i < twikoo_avatar.length; i++) {
            var asrc = twikoo_avatar.eq(i).attr('src');
            if (asrc.search('cn.gravatar.com') != -1) {
                twikoo_avatar.eq(i).attr('src', 'https://hzqyihui.github.io/media/images/comavatar.png');
            }
        }
    }

    function showWechat() {
        var wechat = '';
        alert("博主微信号：" + wechat);
        return false;
    }
</script>

<script>
    /**
     * https://github.com/tangkaichuan/gridea-search
     */
    // 检查缓存是否最新
    function checkCache() {
        var caches_postsnum = localStorage.getItem("postsnum");
        var pt = document.getElementById("side-posttitle").innerHTML;
        pt = pt.replace(/\s+/g, '').replace(/<script>.*?<\/script>/gi, "")
        var ptlen = pt.length;
        var ptlen_storage = localStorage.getItem("poststitlelen");
        if (caches_postsnum != postsnum || ptlen != ptlen_storage) {
            console.log("更新缓存");
            return false
        }
        return true;
    }

    (function () {
        var CACHES = checkCache()
        if (!CACHES) {
            var NOW = Date.now()
            var API_CONTENT = '../api-content/index.html' + '?_=' + NOW
            preload(API_CONTENT);
            getContents(show_getContentresult);
        }

        function show_getContentresult(data) {
            console.log("获取成功");
            var postslen = data["posts"].length;
            localStorage.setItem('postsnum', postslen);
            var poststitlelen = 0;
            var pt = document.getElementById("side-posttitle").innerHTML;
            pt = pt.replace(/\s+/g, '').replace(/<script>.*?<\/script>/gi, "");
            localStorage.setItem('poststitlelen', pt.length);
        }



        // preload
        function preload(url) {
            var preloadLink = document.createElement('link')
            preloadLink.href = url
            preloadLink.rel = 'preload'
            preloadLink.as = 'fetch'
            preloadLink.crossOrigin = 'anonymous'
            document.head.appendChild(preloadLink)
        }

        // 异步 GET 请求
        function get(obj) {
            var xhr = new XMLHttpRequest()
            xhr.open('get', obj.url, true)
            xhr.send(null)
            xhr.onreadystatechange = function () {
                // 异步请求：响应状态为4，数据加载完毕
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        obj.success(xhr.responseText)
                    } else {
                        obj.error(xhr.status)
                    }
                }
            }
        }

        // 获取博客全文 api
        function getContents(callback) {
            if (CACHES) {
                callback(CACHES.contents)
            } else {
                get({
                    url: API_CONTENT,
                    success: function (data) {
                        callback(JSON.parse(data));
                        localStorage.setItem('ContentsCache', data);
                        getStorageContent();
                    }
                })
            }
        }
    })()
    //------------------------核心功能--pjax全局渲染👇-------------------
    let talkinit = false;
    $(document).pjax('a[data-pjax]', '#main', {
        fragment: '#main',
        timeout: 8000
    }).on('pjax:complete', function () {
        //代码块高亮渲染
        document.querySelectorAll('pre code').forEach((block) => {
            hljs.highlightBlock(block);
        });
        //文章热度
        if(postnumChoice){
            getHotnum();
        }
        //文章索引
        toginit();
        //清空搜索栏
        if (searchChoice) {
            searchInit();
            searchInput.value = "";
        }
        var pl = window.location.pathname;
        //说说更新
        if (pl.search("talk") != -1 && !talkinit) {
            add();
            talkinit = true;
        } else if (pl.search("talk") != -1) {
            show();
        }
        if (pl.search("post") != -1) {
            if (shareChoice)
                shareInit();
            if (donateChoice)
                donateInit();
            if ("valine" == "default"&&!commentsHide) {
                $("#comment").hide()
                commentsHide = true
            }
            lazyload();
            codeinit();
            if ("valine" == "twikoo") {
                //twikoo头像更换
                setTimeout(replaceAvatar, 3000);
                setTimeout(replaceAvatar, 8000); //部分博主站点速度加载过慢，再添加一个延迟
            }
        }
        if (btstate == true)
            dropdown();
    }).on('pjax:start', function () {
        $(".pjaxloading").fadeIn(50);
    }).on('pjax:end', function () {
        $(".pjaxloading").fadeOut(50);
    });
    //------------------------核心功能--pjax全局渲染👆-------------------

    //-------------------------------------------------搜索
    // 获取搜索框、搜索按钮、清空搜索、结果输出对应的元素
    if (searchChoice) {
        var searchInput = document.querySelector('.search-input');
        var searchResults = document.querySelector('.search-results');
    }
    // 申明保存文章的标题、链接、内容的数组变量
    var searchValue = '',
        arrItems = [],
        arrLinks = [],
        arrTitles = [],
        arrContents = [],
        arrResults = [],
        indexItem = [],
        itemLength = 0;
    var tmpDiv = document.createElement('div');
    tmpDiv.className = 'result-item';


    function getStorageContent() {
        var data = localStorage.getItem('ContentsCache');
        data = JSON.parse(data);
        posts = data["posts"];
        for (var i = 0; i < posts.length; i++) {
            arrLinks[i] = posts[i].link;
            arrTitles[i] = posts[i].title;
            arrContents[i] = posts[i].content;
            itemLength++;
        }
    }
    if (checkCache())
        getStorageContent();
    // 输入框内容变化后就开始匹配，可以不用点按钮
    // 经测试，onkeydown, onchange 等方法效果不太理想，
    // 存在输入延迟等问题，最后发现触发 input 事件最理想，
    // 并且可以处理中文输入法拼写的变化
    searchInput.oninput = function () {
        setTimeout(searchConfirm, 0);
    }
    searchInput.onfocus = function () {
        searchResults.style.display = 'block';
    }

    function searchConfirm() {
        if (searchInput.value == '') {
            searchResults.style.display = 'none';
        } else if (searchInput.value.search(/^\s+$/) >= 0) {
            // 检测输入值全是空白的情况
            searchInit();
            var itemDiv = tmpDiv.cloneNode(true);
            itemDiv.innerText = '请输入有效内容...';
            searchResults.appendChild(itemDiv);
        } else {
            // 合法输入值的情况
            searchInit();
            searchValue = searchInput.value;
            // 在标题、内容中查找
            searchMatching(arrTitles, arrContents, searchValue);
        }
    }

    // 每次搜索完成后的初始化
    function searchInit() {
        arrResults = [];
        indexItem = [];
        searchResults.innerHTML = '';
        searchResults.style.display = 'block';
    }

    function searchMatching(arr1, arr2, input) {
        // 忽略输入大小写
        input = new RegExp(input, 'i');
        var step = 10;
        // 在所有文章标题、内容中匹配查询值
        for (i = 0; i < itemLength; i++) {
            var indexContent = arr2[i].search(input);
            //标题匹配
            if (arr1[i].search(input) != -1) {
                indexItem.push(i); // 保存匹配值的索引
                if (indexContent != -1) {
                    startindex = (indexContent - step) >= 0 ? (indexContent - step) : 0;
                    var searchcontent = arr2[i].slice(indexContent - step, indexContent + 5)
                    arrResults.push("....." + searchcontent + ".....");
                } else
                    arrResults.push("");
            }
            //内容匹配
            else if (indexContent != -1) {
                indexItem.push(i); // 保存匹配值的索引

                startindex = (indexContent - step) >= 0 ? (indexContent - step) : 0;
                var searchcontent = arr2[i].slice(indexContent - step, indexContent + 5)
                arrResults.push("....." + searchcontent + ".....");
            }
        }

        // 输出总共匹配到的数目
        var totalDiv = tmpDiv.cloneNode(true);
        totalDiv.innerHTML = '<b>总匹配：' + indexItem.length + ' 项<hr></b>';
        searchResults.appendChild(totalDiv);

        // 未匹配到内容的情况
        if (indexItem.length == 0) {
            var itemDiv = tmpDiv.cloneNode(true);
            itemDiv.innerText = '未匹配到内容...';
            searchResults.appendChild(itemDiv);
        }

        // 将所有匹配内容进行组合
        for (i = 0; i < arrResults.length; i++) {
            var itemDiv = tmpDiv.cloneNode(true);
            itemDiv.innerHTML = '<a data-pjax class="searchresults" href="' + arrLinks[indexItem[i]] + '"><b>[' +
                arrTitles[indexItem[i]] +
                ']</b><p>' + arrResults[i] + '</p></a><hr />';
            searchResults.appendChild(itemDiv);
        }
    }
</script>



<!--------------------- 菜单栏图标加载功能👇 ----------------------->
<script>
var menulist = $(".menuicon");
var menulength = menulist.length;
for (var i = 0; i < menulength; i++) {
    var altname = menulist.eq(i).attr("alt");
    var iconurl = icondict[altname];
    if (iconurl == undefined || iconurl == '')
        continue;
    if (iconurl == 'Home') {
        menulist.eq(i).attr("src", "https://hzqyihui.github.io/media/images/Home.png");
menulist.eq(i).show();
} else if (iconurl == 'Archive') {
menulist.eq(i).attr("src", "https://hzqyihui.github.io/media/images/Archive.png");
menulist.eq(i).show();
} else if (iconurl == 'Tag') {
menulist.eq(i).attr("src", "https://hzqyihui.github.io/media/images/Tag.png");
menulist.eq(i).show();
} else if (iconurl == 'About') {
menulist.eq(i).attr("src", "https://hzqyihui.github.io/media/images/About.png");
menulist.eq(i).show();
} else if (iconurl == 'Talk') {
menulist.eq(i).attr("src", "https://hzqyihui.github.io/media/images/Talk.png");
menulist.eq(i).show();
} else if (iconurl == 'Friends') {
menulist.eq(i).attr("src", "https://hzqyihui.github.io/media/images/Friends.png");
menulist.eq(i).show();
} else if (iconurl == 'Fly') {
menulist.eq(i).attr("src", "https://hzqyihui.github.io/media/images/wind.png");
menulist.eq(i).show();
} else {
menulist.eq(i).attr("src", iconurl);
menulist.eq(i).show();
}
}
</script>
<!--------------------- 菜单栏图标加载功能👆 ----------------------->

<script>
    //--------------------bber-onetalk加载👇--------------------------------
    function bberonetalkLoad(){
        bberjsonurl = '';
        if ( $("#bber-talk").length > 0 ) {
        jsonUrl = bberjsonurl
        $.getJSON(jsonUrl+"?t="+Date.parse( new Date()),function(res){
        var bberHtml = ''
        $.each(res.data, function(i, item){
            d = new Date(item.date)
            date = d.getFullYear()+'/'+(d.getMonth()+1)+'/'+d.getDate() +' '+d.getHours()+':'+d.getMinutes()+':'+d.getSeconds()
            dataTime = '<span class="datatime">'+date+'</span>'
            bberHtml += '<li class="item item-'+(i+1)+'">'+dataTime+'： <a data-pjax href="https://hzqyihui.github.io/bber">'+item.content+'</a></li>'
        });
        $('#bber-talk').append('<span class="index-talk-icon"><svg viewBox="0 0 1024 1024" width="21" height="21"><path d="M184.32 891.667692c-12.603077 0-25.206154-2.363077-37.809231-7.876923-37.021538-14.966154-59.864615-49.624615-59.864615-89.009231v-275.692307c0-212.676923 173.292308-385.969231 385.969231-385.969231h78.76923c212.676923 0 385.969231 173.292308 385.969231 385.969231 0 169.353846-137.846154 307.2-307.2 307.2H289.083077l-37.021539 37.021538c-18.904615 18.116923-43.323077 28.356923-67.741538 28.356923zM472.615385 195.347692c-178.018462 0-322.953846 144.935385-322.953847 322.953846v275.692308c0 21.267692 15.753846 29.144615 20.48 31.507692 4.726154 2.363077 22.055385 7.876923 37.021539-7.08923l46.473846-46.473846c6.301538-6.301538 14.178462-9.452308 22.055385-9.452308h354.461538c134.695385 0 244.184615-109.489231 244.184616-244.184616 0-178.018462-144.935385-322.953846-322.953847-322.953846H472.615385z"></path><path d="M321.378462 512m-59.076924 0a59.076923 59.076923 0 1 0 118.153847 0 59.076923 59.076923 0 1 0-118.153847 0Z"></path><path d="M518.301538 512m-59.076923 0a59.076923 59.076923 0 1 0 118.153847 0 59.076923 59.076923 0 1 0-118.153847 0Z"></path><path d="M715.224615 512m-59.076923 0a59.076923 59.076923 0 1 0 118.153846 0 59.076923 59.076923 0 1 0-118.153846 0Z"></path></svg></span><ul class="talk-list">'+bberHtml+'</ul>')
        //Lately({ 'target': '#bber-talk .datatime' });
        });
        function Roll (){
        var list_li = $('.talk-list li'),cur_li = list_li.first(),last_li = list_li.last();
        last_li.after(cur_li);
        };
        setInterval(Roll,3000);
    }
    }
  //--------------------bber-onetalk加载👆--------------------------------
</script>

<script>
    var ClicksideState = 1
    var cur_readingmode = false
    function readingmode(){
        if (!cur_readingmode){
            if(menupos == 'left'){
                if(ClicksideState==1)
                menu_openclose1()
            }
            $("#tab1").fadeOut(50)
            $("#bg").fadeOut(50)
            $(".navbar").fadeOut(50)
            $(".markdownIt-TOC li a").css("color","black")
            cur_readingmode = true
        }else{
            if(menupos == 'left'){
                if(ClicksideState==0)
                    menu_openclose1()
            }
            $("#tab1").fadeIn(50)
            $("#bg").fadeIn(50)
            $(".navbar").fadeIn(50)
            $(".markdownIt-TOC li a").css("color","antiquewhite")
            cur_readingmode = false  
        }
    }
</script>


<!----夜间模式👇-->
<script>
    var moonlightState = false
    function moonlight(){
        if(!moonlightState){
            $("#bg").fadeOut(200)
            $('body').css("background","rgba(0,0,0,0.8)")
            $(".markdownIt-TOC li a").css("color","white")
            $(".text p, .text ul").css("color","white")
            $("#articlecontent").css("color","white")
            $(".post-copyright").css("background","rgba(10,10,10,0.5)")
            $('blockquote').css("background","rgba(10,10,10,0.6)")
            $("#articlecontent").css("background","rgba(10,10,10,0.5)")
            moonlightState = true
            $(".moon").attr("src","https://hzqyihui.github.io/media/images/sun.png")
        }else{
            $("#bg").show()
            $(".markdownIt-TOC li a").css("color","antiquewhite")
            $(".text p, .text ul").css("color","rgba(0, 0, 0, 0.7)")
            $("#articlecontent").css("color","black")
            $(".post-copyright").css("background","#f9f9f9")
            $('blockquote').css("background","#eff8f0")
            $("#articlecontent").css("background","white")
            $(".moon").attr("src","https://hzqyihui.github.io/media/images/moon.png")
            $('body').css("background","white")
            moonlightState = false
        }
    }
</script>
<!----夜间模式👆--></div>
            <!--------------- pc端菜单栏👆 ---------------->

            <!-- ----------------左侧菜单栏html 👇-------------------------------------->
            
            <div>
                <div id="tab1" class="tab">
                    <div class="bars">
                        <span></span>
                        <span></span>
                        <span></span>
                    </div>
                    <div class="close"></div>
                </div>
                <div class="col-md-1 col-lg-3"></div>
                <!-- main标签，pjax渲染时加载以下内容👇 -->
                <div id="main" class="col-xs-12 col-sm-12 col-md-12 col-lg-7" style="padding:0;">
                    <div class="readingmode" onclick="readingmode()">阅</div>
                    <link rel="stylesheet" href="https://hzqyihui.github.io/media/css/font-awesome.css">

<body>
    <div class="allcontent" id="postdetail">
        <div class="postshow postpadding" style="padding-bottom: 0;">
            
            <div class="postdetailimg">
                <img src="https://oss.maxhua.cn/surface_cover/v2-904505bcf0c424788f6028b8952aa2e7_720w.jpeg" class="postimage" style="cursor:auto">
            </div>
            <div class="postinfo-detail">
                <div class="postdate"><i class="fa fa-calendar"></i>2022-09-11</div>
                <div class="poststatus postdate"><i class="fa fa-clock-o"></i> 14 min read</div>
                <div class="posttag">
                    
                    <a data-pjax href="https://hzqyihui.github.io/tag/O5yPFtuXM/" class="postlink">
                        <i class="fa fa-tag"></i> 事务
                    </a>
                    
                    <a data-pjax href="https://hzqyihui.github.io/tag/LKYLTZlP_/" class="postlink">
                        <i class="fa fa-tag"></i> 分布式事务
                    </a>
                    
                </div>
            </div>
            
        <div id="articlecontent">
            <div id="texttitle" style="text-align: center">
                <h2 id="ptitle">常见分布式事务解决方案：</h2>
                <!-- id 将作为查询条件 -->
                <div id="pl" style="display:none">https://hzqyihui.github.io/post/chang-jian-fen-bu-shi-shi-wu-jie-jue-fang-an/</div>
                <div id="rootaddr" style="display:none">https://hzqyihui.github.io</div>
                
                    
                    <span id="hotnum">
                        <h4><i class="fa fa-eye" aria-hidden="true" style="color: #FF8C00;"></i>&nbsp;&nbsp;热度</i>: <i
                                class="hotnum">loading...</i></h4>
                    </span>
                    
                
            </div>
            <div class="text ">
                <h1 id="常见分布式事务解决方案">常见分布式事务解决方案</h1>
<ul>
<li>两阶段提交(2PC,Two-phase Commit)</li>
<li>TCC补偿模式</li>
<li>基于本地消息表实现最终一致性</li>
<li>最大努力通知</li>
<li>基于可靠消息最终一致性方案</li>
</ul>
<h2 id="1-两阶段提交2pc">1、两阶段提交(2PC)</h2>
<p>两阶段提交又称<code>2PC</code>,<code>2PC</code>是一个非常经典的<code>中心化的原子提交协议</code>。<br>
这里所说的中心化是指协议中有两类节点：一个是<code>中心化协调者节点(coordinator)</code>和<code>N个参与者节点(partcipant)</code>。<br>
两个阶段：第一阶段：<code>投票阶段</code> 和第二阶段：<code>提交/执行阶段</code>。<br>
举例 <code>订单服务A</code>,需要调用 <code>支付服务B</code> 去支付，支付成功则处理购物订单为待发货状态，否则就需要将购物订单处 理为失败状态。<br>
那么看2PC阶段是如何处理的</p>
<h3 id="11-第一阶段投票阶段">1.1、第一阶段：投票阶段</h3>
<p>第一阶段主要分为3步</p>
<ol>
<li>事务询问<br>
协调者向所有的参与者发送事务预处理请求，称之为Prepare,并开始等待各参与者的响应。</li>
<li>执行本地事务<br>
各个参与者节点执行本地事务操作，但在执行完成后并不会真正提交数据库本地事务，而是先向协调者报告说：“我这边可以处理了/我这边不能处理”。·</li>
<li>各参与者向协调者反馈事务询的响应<br>
如果参与者成功执行了事务操作，那么就反馈给协调者Ys响应，表示事务可以执行，如果没有参与者成功执行事务，那么就反馈给协调者N。响应，表示事务不可以执行。<br>
第一阶段执行完后，会有两种可能。1、所有都返回Ys.2、有一个或者多个返回No.</li>
</ol>
<h3 id="12-第二阶段提交执行阶段成功流程">1.2、第二阶段：提交/执行阶段（成功流程）</h3>
<p>成功条件：所有参与者都返回Yes。<br>
第二阶段主要分为两步</p>
<ol>
<li>所有的参与者反馈给协调者的信息都是Yes,那么就会执行事务提交<br>
协调者向所有参与者节点发出Commit请求.</li>
<li>事务提交<br>
参与者收到Commiti请求之后，就会正式执行本地事务Commit操作，并在完成提交之后释放整个事务执行期间占用的事务资源。</li>
</ol>
<h3 id="13-第二阶段提交执行阶段异常流程">1.3、第二阶段：提交/执行阶段（异常流程）</h3>
<p>异常条件：任何一个参与者向协调者反馈了N。响应，或者等待超时之后，协调者尚未收到所有参与者的反馈响应。<br>
异常流程第二阶段也分为两步</p>
<ol>
<li>发送回滚请求<br>
协调者向所有参与者节点发出RoollBack请求</li>
<li>事务回滚<br>
参与者接收到RoollBack清求后，会回滚本地事务。</li>
</ol>
<h3 id="14-2pc缺点">1.4、2PC缺点</h3>
<p>通过上面的演示，很容易想到2pc所带来的缺陷</p>
<ol>
<li>性能问题<br>
无论是在第一阶段的过程中，还是在第二阶段，所有的参与者资源和协调者资源都是被锁住的，只有当所有节点准备完毕，事务协调者才会通知进行全局提交，参与者进行本地事务提交后才会释放资源。这样的过程会比较漫长，对性能影响比较大。</li>
<li>单节点故障<br>
由于协调者的重要性，一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段，协调者发生故障，那么所有的参与者还都处于锁定事务资源的状态中，而无法继续完成事务操作。（虽然协调者挂掉，可以重新选举一个协调者，但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)<br>
<strong>2PC出现单点问题的三种情况</strong>
<ul>
<li>协调者正常，参与者宕机<br>
由于协调者无法收集到所有参与者的反馈，会陷入阻塞情况。<br>
解决方案：引入超时机制，如果协调者在超过指定的时间还没有收到参与者的反馈，事务就失败，向所有节点发送终止事务请求</li>
<li>协调者宕机，参与者正常<br>
无论处于哪个阶段，由于协调者宕机，无法发送提交请求，所有处于执行了操作但是未提交状态的参与者都会陷入</li>
</ul>
</li>
</ol>
<p>阻塞情况<br>
解决方案：引入协调者备份，同时协调者需记录操作日志.当检测到协调者宕机一段时间后，协调者备份取代协调者，并读取操作日志，向所有参与者询问状态。<br>
- 协调者和参与者都宕机<br>
1. 发生在第一阶段：因为第一阶段，所有参与者都没有真正执行commit,所以只需重新在剩余的参与者中重新选出一个协调者，新的协调者在重新执行第一阶段和第二阶段就可以了。<br>
2. 发生在第二阶段并且挂了的参与者在挂掉之前没有收到协调者的指令。也就是上面的第4步挂了，这是可能协调者<br>
还没有发送第4步就挂了。这种情形下，新的协调者重新执行第一阶段和第二阶段操作。<br>
3. 发生在第二阶段并且有部分参与者已经执行完commit操作。就好比这里订单服务A和支付服务B都收到协调者发送的commitf信息，开始真正执行本地事务commit,.但突发情况，A commit成功，B确挂了。这个时候目前来讲数据时不一致的。</p>
<h2 id="2-tcc分布式事务名字含义-try-confirm-或-cancel">2、TCC分布式事务（名字含义： Try、Confirm 或 Cancel）</h2>
<p><img src="https://oss.maxhua.cn/transaction/tcc.png" alt="" loading="lazy"><br>
一个订单支付之后，我们需要做下面的步骤：</p>
<ul>
<li>更改订单的状态为“已支付”</li>
<li>扣减商品库存</li>
<li>给会员增加积分</li>
<li>创建销售出库单通知仓库发货<br>
<img src="https://oss.maxhua.cn/transaction/tcc_order.png" alt="" loading="lazy"><br>
好，业务场景有了，现在我们要更进一步，实现一个TCC分布式事务的效果。<br>
什么意思呢？也就是说：<br>
[1]订单服务-修改订单状态<br>
[2]库存服务-扣减库存<br>
[3]积分服务-增加积分<br>
[4]仓储服务-创建销售出库单。<br>
上述这几个步骤，要么一起成功，要么一起失败，必须是一个整体性的事务。</li>
</ul>
<p>举个例子，现在订单的状态都修改为“已支付”了，结果库存服务扣减库存失败。那个商品的库存原来是100<br>
件，现在卖掉了2件，本来应该是98件了。结果呢？由于库存服务操作数据库异常，导致库存数量还是100。这不是在坑人么，当然不能允许这种情况发生了！</p>
<p>但是如果你不用TCC分布式事务方案的话，就用个go开发这么一个微服务系统，很有可能会干出这种事儿来。<br>
我们来看看下面的这个图，直观的表达了上述的过程：<br>
<img src="https://oss.maxhua.cn/transaction/tcc_order_2.png" alt="" loading="lazy"><br>
所以说，我们有必要使用TCC分布式事务机制来保证各个服务形成一个整体性的事务。<br>
上面那几个步骤，要么全部成功，如果任何一个服务的操作失败了，就全部一起回滚，撤销已经完成的操作。<br>
比如说库存服务要是扣减库存失败了，那么订单服务就得撤销那个修改订单状态的操作，然后得停止执行增加积分和通知出库两个操作。<br>
说了那么多，老规矩，给大家上一张图，大伙儿顺着图来直观的感受一下：<br>
<img src="https://oss.maxhua.cn/transaction/tcc_order_3.png" alt="" loading="lazy"></p>
<h3 id="落地实现tcc分布式事务">落地实现TCC分布式事务</h3>
<p>那么现在到底要如何来实现一个TCC分布式事务，使得各个服务，要么一起成功？要么一起失败呢？大家稍安勿躁，我们这就来一步一步的分析一下。咱们就以一个go开发系统作为背景来解释。</p>
<h4 id="tcc实现阶段一try">TCC实现阶段一：Try</h4>
<h4 id="首先订单服务那儿他的代码大致来说应该是这样">首先，订单服务那儿，他的代码大致来说应该是这样</h4>
<pre><code>type OrderService struct{
    CreditSrvclient proto.Creditclient
    WmsSrvclient proto.Wmsclient
    InventorySrvClient proto.InventoryClient
}
func NewOrderService()*OrderService{
    return &amp;OrderService{
        CreditSrvclient:proto.Creditclient{},
        WmsSrvclient:proto.Wmsclient{},
        InventorySrvClient:proto.Inventoryclient{},
    }
}
func (o OrderService)UpdateOrderstatus()error{
    return nil
}
func (o OrderService)Notify()error{
    o.UpdateOrderStatus()  //更新订单状态
    o.CreditSrvclient.AddCredit()  //增加积分
    o.InventorySrvclient.ReduceStock() //库存确认扣减
    o.Wmsclient.SaleDelivery()  //记录仓库变更记录
    return nil
}
</code></pre>
<h4 id="总结与思考">总结与思考</h4>
<p>总结一下，你要玩儿TCC分布式事务的话：</p>
<ol>
<li>首先需要选择某种TCC分布式事务框架，各个服务里就会有这个TCC分布式事务框架在运行。</li>
<li>然后你原本的一个接口，要改造为3个逻辑，Try-Confirm-Cancel。
<ul>
<li>先是服务调用链路依次执行Try逻辑</li>
<li>如果都正常的话，TCC分布式事务框架推进执行Confirm逻辑，完成整个事务</li>
<li>如果某个服务的Try逻辑有问题，TCC分布式事务框架感知到之后就会推进执行各个服务的Cancel逻辑，撤销之前执行的各种操作。</li>
<li>这就是所谓的TCC分布式事务。</li>
<li>TCC分布式事务的核心思想，说白了，就是当遇到下面这些情况时，</li>
</ul>
</li>
<li>某个服务的数据库宕机了</li>
<li>某个服务自己挂了</li>
<li>那个服务的redis、elasticsearch、MQ等基础设施故障了</li>
<li>某些资源不足了，比如说库存不够这些
<ul>
<li>先来Ty一下，不要把业务逻辑完成，先试试看，看各个服务能不能基本正常运转，能不能先冻结我需要的资源。</li>
<li>如果Try都ok,也就是说，底层的数据库、redis、elasticsearch、MQ都是可以写入数据的，并且你保留好了需要使用的一些资源（比如冻结了一部分库存）。</li>
<li>接着，再执行各个服务的Confirm逻辑，基本上Confirm就可以很大概率保证一个分布式事务的完成了。</li>
<li>那如果Try阶段某个服务就失败了，比如说底层的数据库挂了，或者redis挂了，等等。</li>
<li>此时就自动执行各个服务的Canceli逻辑，把之前的Try逻辑都回滚，所有服务都不要执行任何设计的业务逻辑。保证大家要么一起成功，要么一起失败。</li>
</ul>
</li>
</ol>
<h4 id="终极大招">终极大招</h4>
<ul>
<li>如果有一些意外的情况发生了，比如说订单服务突然挂了，然后再次重启，TCC分布式事务框架是如何保证之前没执行完的分布式事务继续执行的呢？ -- TCC事务管理器，事务日志</li>
<li>TCC事务框架都是要记录一些分布式事务的活动日志的，可以在磁盘上的日志文件里记录，也可以在数据库里记录。保存下来分布式事务运行的各个阶段和状态。</li>
<li>万一某个服务的Cancel或者Confirm逻辑执行一直失败怎么办呢？</li>
<li>那也很简单，TCC事务框架会通过活动日志记录各个服务的状态。</li>
<li>
<ul>
<li>举个例子，比如发现某个服务的Cancel或者CΦnfirm一直没成功，会不停的重试调用他的Cancel或者Confirmi逻辑，务必要他成功！</li>
</ul>
</li>
<li>当然了，如果你的代码没有写什么bug,有充足的测试，而且Ty阶段都基本尝试了一下，那么其实一般Confirm、Cancel都是可以成功的！</li>
<li>如果实在解决不了，那么这个一定是很小概率的事件，这个时候发邮件通知人工处理</li>
<li>seata(阿里java)，  go-seata， 现在的开源TCC框架</li>
</ul>
<h4 id="tcc优缺点">TCC优缺点</h4>
<p>优点：</p>
<ol>
<li>解决了跨服务的业务操作原子性问题，例如组合支付，订单减库存等场景非常实用</li>
<li>TCC的本质原理是把数据库的二阶段提交上升到微服务来实现，从而避免了数据库2阶段中锁冲突的长事务低性能风险。</li>
<li>TCC异步高性能，它采用了try先检查，然后异步实现confirm,真正提交的是在confirm方法中。<br>
缺点：</li>
<li>对微服务的侵入性强，微服务的每个事务都必须实现try,confirm,cancel等3个方法，开发成本高，今后维护改造的成本也高。</li>
<li>为了达到事务的一致性要求，try,confirm、cancel接口必须实现等幂性操作。(定时器+重试)</li>
<li>由于事务管理器要记录事务日志，必定会损耗一定的性能，并使得整个TCC事务时间拉长，建议采用redis的方式来记录事务日志。</li>
<li>tcc需要通过锁来确保数据的一致性，会加锁导致性能不高</li>
</ol>
<h2 id="3-基于本地消息表的最终一致性">3. 基于本地消息表的最终一致性</h2>
<p><img src="https://oss.maxhua.cn/transaction/local_msg.png" alt="" loading="lazy"><br>
本地消息表方案<br>
本地消息表这个方案最初是eBay提出的，此方案的核心是通过本地事务保证数据业务操作和消息的一致性，然后通过定时任务将消息发送至消息中间件，待确认消息发送给消费方成功再将消息删除。<br>
下面以注册送积分为例来说明：<br>
下例共有两个微服务交互，用户服务和积分服务，用户服务负责添加用户，积分服务负责增加积分。<br>
<img src="https://oss.maxhua.cn/transaction/local_msg_2.png" alt="" loading="lazy"></p>
<h4 id="31-用户注册">3.1、用户注册</h4>
<p>用户服务在本地事务新增用户和增加“积分消息日志”。（用户表和消息表通过本地事务保证一致）<br>
下表是伪代码</p>
<pre><code>begin transaction;
//1.新增用户
//2.存储积分消息日志
commit transation;
</code></pre>
<p>这种情况下，本地数据库操作与存储积分消息日志处于同一事务中，本地数据库操作与记录消息日志操作具备原子<br>
性。</p>
<h4 id="32-定时任务扫描日志">3.2、定时任务扫描日志</h4>
<p>如何保证将消息发送给消息队列呢？<br>
经过第一步消息已经写到消息日志表中，可以启动独立的线程，定时对消息日志表中的消息进行扫描并发送至消息中间件，在消息中间件反馈发送成功后删除该消息日志，否则等待定时任务下一周期重试。</p>
<h4 id="33-消费消息">3.3、消费消息</h4>
<p>如何保证消费者一定能消费到消息呢？<br>
这里可以使用MQ的ack(即消息确认)机制，消费者监听MQ,如果消费者接收到消息并且业务处理完成后向MQ发送ack(即消息确认)，此时说明消费者正常消费消息完成，MQ将不再向消费者推送消息，否则消费者会不断重试向消费者来发送消息。<br>
积分服务接收到“增加积分”消息，开始增加积分，积分增加成功后消息中间件回应ack,否则消息中间件将重复投递此消息。<br>
由于消息会重复投递，积分服务的“增加积分”功能需要实现幂等性。</p>
<p><code>总结</code>：上诉的方式是一种非常经典的实现，基本避免了分布式事务，实现了“最终一致性”。但是，关系型数据库的吞吐量和性能方面存在瓶颈，频繁的读写消息会给数据库造成压力。所以，在真正的高并发场景下，该方案也会有瓶颈和限制的。</p>
<h2 id="4-基于可靠消息的最终一致性-较为流行的方案">4. 基于可靠消息的最终一致性 （较为流行的方案）</h2>
<p>RabbitMQ 和  RocketMQ 都支持<code>半消息机制</code>， 但是 RocketMQ更关键的一点是支持事务消息回查。<br>
<img src="https://oss.maxhua.cn/transaction/final_msg.png" alt="" loading="lazy"></p>
<h2 id="5-最大努力通知方案">5. 最大努力通知方案</h2>
<p>某些特殊系统需要的方式：如支付宝支付<br>
<img src="https://oss.maxhua.cn/transaction/hard_try.png" alt="" loading="lazy"></p>

            </div>
            <hr style="width: 80%;">
            
            <div class="donateContainer">
                <span class="donatebtn">赏</span>
            </div>
            <div class="donateContainer">
                <img src="https://oss.maxhua.cn/Wechat_shoukuanma.png" alt="" class="donate">
            </div>
            
            <div class="postfooter">
                <ul class="post-copyright">
                    <li><strong>文章作者：</strong>逸晖</li>
            <li><strong>原文链接：</strong>https://hzqyihui.github.io/post/chang-jian-fen-bu-shi-shi-wu-jie-jue-fang-an/</li>
            <li><strong>版权声明：</strong>本博客所有文章除特别声明外，均采用<i class="fa fa-fw fa-creative-commons"></i>BY-NC-SA
                许可协议。转载请注明出处！</li>
            </ul>
        </div>
        
            <div class="shareContainer">
                <div class="share" style="height:30px;">
                    <a class="qq" href=""><i><img class="shareicon"
                                src="https://hzqyihui.github.io/media/images/qqs.png" alt=""></i></a>
                    <a class="wechat" href=""><i><img class="shareicon"
                                src="https://hzqyihui.github.io/media/images/wechats.png" alt=""></i></a>
                    <a class="weibo" href=""><i><img class="shareicon"
                                src="https://hzqyihui.github.io/media/images/weibos.png" alt=""></i></a>
                    <a class="qzone" href=""><i><img class="shareicon"
                                src="https://hzqyihui.github.io/media/images/qzones.png" alt=""></i></a>
                    <a class="douban" href=""><i><img class="shareicon"
                                src="https://hzqyihui.github.io/media/images/doubans.png" alt=""></i></a>
                    <a class="facebook" href=""><i><img class="shareicon"
                                src="https://hzqyihui.github.io/media/images/facebooks.png" alt=""></i></a>
                    <a class="twitter" href=""><i><img class="shareicon"
                                src="https://hzqyihui.github.io/media/images/twitter.png" alt=""></i></a>
                </div>
            </div>
            
        <div id="otherpost">
            
            <a  data-pjax class="prev-post-pc" href="https://hzqyihui.github.io/post/rocket-an-zhuang-he-pei-zhi/"  style="color: black;">
                <div id="ppcontainer">
                    
                    <img src="https://oss.maxhua.cn/surface_cover/django-kai-fa-ge-ren-bo-ke-xi-tong.png" style="width: 100%;" class="opimg">
                    
                    <h4 class="optitle">Rocket安装和配置</h4>
                </div>
            </a>
            <div class="prev-post">
                上一篇 <a  data-pjax href="https://hzqyihui.github.io/post/rocket-an-zhuang-he-pei-zhi/">Rocket安装和配置</a>
            </div>
            
            
            <a data-pjax class="next-post-pc" href="https://hzqyihui.github.io/post/cap-he-base-li-lun/"  style="color: black;">
                <div id="npcontainer">
                    
                    <img src="https://oss.maxhua.cn/surface_cover/git-ming-ling-xing-ri-chang-zhi-ling.jpeg" style="width: 100%;" class="opimg">
                    
                    <h4 class="optitle">CAP和BASE理论：</h4>
                </div>
            </a>
            <div class="next-post">
                下一篇 <a  data-pjax href="https://hzqyihui.github.io/post/cap-he-base-li-lun/">CAP和BASE理论：</a>
            </div>
            
        </div>
    </div>
    </div>
    </div>
    <!-- 弹窗 -->
    <div id="myModal" class="modal">
        <img class="modal-content" id="img01">
    </div>
</body>
<script>
    //文章阅读热度
    var pl = $("#pl").html();
    var rootaddr = $("#rootaddr").html();
    pl = pl.replace(rootaddr, "");
    $("#hotnum").attr('id', pl);
</script>
                    <div class="toc-container">
                        <ul class="markdownIt-TOC">
<li><a href="#%E5%B8%B8%E8%A7%81%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88">常见分布式事务解决方案</a>
<ul>
<li><a href="#1-%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A42pc">1、两阶段提交(2PC)</a>
<ul>
<li><a href="#11-%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5%E6%8A%95%E7%A5%A8%E9%98%B6%E6%AE%B5">1.1、第一阶段：投票阶段</a></li>
<li><a href="#12-%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4%E6%89%A7%E8%A1%8C%E9%98%B6%E6%AE%B5%E6%88%90%E5%8A%9F%E6%B5%81%E7%A8%8B">1.2、第二阶段：提交/执行阶段（成功流程）</a></li>
<li><a href="#13-%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4%E6%89%A7%E8%A1%8C%E9%98%B6%E6%AE%B5%E5%BC%82%E5%B8%B8%E6%B5%81%E7%A8%8B">1.3、第二阶段：提交/执行阶段（异常流程）</a></li>
<li><a href="#14-2pc%E7%BC%BA%E7%82%B9">1.4、2PC缺点</a></li>
</ul>
</li>
<li><a href="#2-tcc%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E5%90%8D%E5%AD%97%E5%90%AB%E4%B9%89-try-confirm-%E6%88%96-cancel">2、TCC分布式事务（名字含义： Try、Confirm 或 Cancel）</a>
<ul>
<li><a href="#%E8%90%BD%E5%9C%B0%E5%AE%9E%E7%8E%B0tcc%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1">落地实现TCC分布式事务</a>
<ul>
<li><a href="#tcc%E5%AE%9E%E7%8E%B0%E9%98%B6%E6%AE%B5%E4%B8%80try">TCC实现阶段一：Try</a></li>
<li><a href="#%E9%A6%96%E5%85%88%E8%AE%A2%E5%8D%95%E6%9C%8D%E5%8A%A1%E9%82%A3%E5%84%BF%E4%BB%96%E7%9A%84%E4%BB%A3%E7%A0%81%E5%A4%A7%E8%87%B4%E6%9D%A5%E8%AF%B4%E5%BA%94%E8%AF%A5%E6%98%AF%E8%BF%99%E6%A0%B7">首先，订单服务那儿，他的代码大致来说应该是这样</a></li>
<li><a href="#%E6%80%BB%E7%BB%93%E4%B8%8E%E6%80%9D%E8%80%83">总结与思考</a></li>
<li><a href="#%E7%BB%88%E6%9E%81%E5%A4%A7%E6%8B%9B">终极大招</a></li>
<li><a href="#tcc%E4%BC%98%E7%BC%BA%E7%82%B9">TCC优缺点</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#3-%E5%9F%BA%E4%BA%8E%E6%9C%AC%E5%9C%B0%E6%B6%88%E6%81%AF%E8%A1%A8%E7%9A%84%E6%9C%80%E7%BB%88%E4%B8%80%E8%87%B4%E6%80%A7">3. 基于本地消息表的最终一致性</a><br>
*
<ul>
<li><a href="#31-%E7%94%A8%E6%88%B7%E6%B3%A8%E5%86%8C">3.1、用户注册</a></li>
<li><a href="#32-%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E6%89%AB%E6%8F%8F%E6%97%A5%E5%BF%97">3.2、定时任务扫描日志</a></li>
<li><a href="#33-%E6%B6%88%E8%B4%B9%E6%B6%88%E6%81%AF">3.3、消费消息</a></li>
</ul>
</li>
<li><a href="#4-%E5%9F%BA%E4%BA%8E%E5%8F%AF%E9%9D%A0%E6%B6%88%E6%81%AF%E7%9A%84%E6%9C%80%E7%BB%88%E4%B8%80%E8%87%B4%E6%80%A7-%E8%BE%83%E4%B8%BA%E6%B5%81%E8%A1%8C%E7%9A%84%E6%96%B9%E6%A1%88">4. 基于可靠消息的最终一致性 （较为流行的方案）</a></li>
<li><a href="#5-%E6%9C%80%E5%A4%A7%E5%8A%AA%E5%8A%9B%E9%80%9A%E7%9F%A5%E6%96%B9%E6%A1%88">5. 最大努力通知方案</a></li>
</ul>
</li>
</ul>

                    </div>
                    <div class="tog">
                        <div class="toggleContainer">
                            <div class="toggle">
                                <i class="fas fa-angle-double-up"></i>
                            </div>
                        </div>
                    </div>
                    <!---------------------------- 评论系统👇 ------------------------->
                    <div id="comment" class="comment">
                        <div class="commentcontainer">
                            
                                
                                    
        <div id="vcomments">
        </div>
        <script>
            var valine = new Valine();
            var curpath = window.location.pathname;
            if (curpath[curpath.length - 1] != '/')
                curpath += '/'
            valine.init({
                el: '#vcomments',
                appId: '6kX8OLncEr2WFzTCkIaiZzkj-gzGzoHsz',
                appKey: 'nSVcrCBD1jAuhDiW4uhz2szQ',
                avatar: 'retro',
                pageSize: 10,
                placeholder: '既然来了，何不畅所欲言呢？',
                recordIP: true,
                path: curpath //[重点] 记得添加path路径
            });
        </script>
        <script type="text/javascript">
            var jrsc = '既然来了，何不畅所欲言呢？';
            jinrishici.load(function (result) {
                // 自己的处理逻辑
                jrsc = result.data.content + "\n「" + result.data.origin.title + "」" + result.data.origin
                    .dynasty + " · " + result.data.origin.author;
            });
        </script>


                                    <div class="clear"></div>
                                
                            
                        </div>
                    </div>
                    <!---------------------------- 评论系统👆 ------------------------->
                </div>
                <!-- main标签，pjax渲染时加载以上内容👆 -->
                <div class="col-md-1 col-lg-2"></div>
                <div id="bg">
                </div>
            </div>
            <!-- ----------------左侧菜单栏html 👆-------------------------------------->
            
        </div>
        <!-- aplayer播放器加载👇 -->
            
            <head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">
</head>

<body>
    <div class="miniMode">
        <meting-js id="myaplayer" server="netease" type="playlist" fixed="true" order="default" muted="muted">
        </meting-js>
    </div>
</body>


<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
<script>
    function aplayerInit() {
        var aplayerid = "2025151208";
        var aplayerserver = "netease";
        var aplayerorder = "random";
        var aplayerautoplay = "false";
        var aplayer = $("#myaplayer");
        aplayer.attr("server", aplayerserver);
        aplayer.attr("id", aplayerid);
        aplayer.attr("order", aplayerorder);
        if (aplayerautoplay == "true")
            aplayer.attr("autoplay", "true");
    }
    aplayerInit();
</script>

<script src="https://cdn.jsdelivr.net/gh/850552586/ericamcdn@0.1/js/Aplayer.min.js"></script>
<!-- require MetingJS -->
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>

<script>
    function aplayerbtnInit() {
        var player = document.querySelectorAll('meting-js')[0].aplayer;
        if(player==undefined)return false;
        player.lrc.hide();
        var mbabtn = $("#mbaplayer");
        if (player.paused == false) {
            mbabtn.removeClass("fa-play-circle-o");
            mbabtn.addClass("fa-pause-circle");
        } else {
            mbabtn.addClass("fa-play-circle-o");
            mbabtn.removeClass("fa-pause-circle");
        }
    }

    function aplayerPlay() {
        var player = document.querySelectorAll('meting-js')[0].aplayer;
        var mbabtn = $("#mbaplayer");
        if (player.paused == true) {
            player.play();
            mbabtn.removeClass("fa-play-circle-o");
            mbabtn.addClass("fa-pause-circle");
        } else {
            player.pause();
            mbabtn.addClass("fa-play-circle-o");
            mbabtn.removeClass("fa-pause-circle");
        }
    }
</script>
            
        <!-- aplayer播放器加载👆 -->
    </div>
    <!-- 背景图加载方式👇 -->
    <div id="bgchoice" style="display: none">default
                </div>
                
                <div id="bgurl" style="display:none">/media/images/custom-bgimage.jpg</div>
                
    <!-- 背景图加载方式👆 -->
</body>

<script>
    //初始化
    //更换twikoo头像
    setTimeout(replaceAvatar,1000);
    setTimeout(replaceAvatar,2000);
    setTimeout(replaceAvatar,8000);
    // --------为每张图片添加懒加载（lazyload）标签-->懒加载功能 👇------------
    lazyload();
    //为文章内所有代码块添加代码复制功能 👇
    codeinit();
    // ------代码高亮显示功能-------------
    hljs.initHighlightingOnLoad();
    if (shareChoice)
        shareInit();
    if (donateChoice)
        donateInit();
</script>

<!------------------ 加载live2d模型功能（看板娘）👇 ---------------->

<script type="text/javascript">
    var message_Path = '/live2d/'
    var home_Path = document.getElementById("domainname").innerHTML + "/"; //此处修改为你的域名，必须带斜杠
</script>
<script type="text/javascript" src="https://hzqyihui.github.io/media/js/live2d.js"></script>
<script type="text/javascript" src="https://hzqyihui.github.io/media/live2d/js/message.js"></script>
<script type="text/javascript">
    loadlive2d("live2d", "https://hzqyihui.github.io/media/live2d/assets/tororo.model.json");
</script>

<!------------------ 加载live2d模型功能（看板娘）👆 ---------------->


<!----------------------- 返回顶部按钮功能👇 --------------------->
<script>
    $(function () {
        $('.toggleContainer').click(function () {
            $('html,body').animate({
                scrollTop: '0px'
            }, 800);
        });
        $(window).scroll(function () {
            var st = $(window).scrollTop();
            if (st > 30) {
                $(".toggleContainer").fadeIn(400);
            } else {
                $(".toggleContainer").fadeOut(100);
            }
        });
    });
</script>
<!----------------------- 返回顶部按钮功能👆 --------------------->


<!------------ 加载全局背景图功能👇（开始时没有写好，这功能有点鸡肋/(ㄒoㄒ)/~~） -->
<script>
    var bgchoice = "default"
    var bg = $('#bg');
    var bgurl = document.getElementById("bgurl").innerHTML;
    if (bgchoice == 'default')
    {
        for (var i = 0; i < 3; i++)
            bgurl = bgurl.replace("\\", "/");
        bgurl = "https://hzqyihui.github.io/" + bgurl;
    }
    bg.css("background", "url('" + bgurl + "')");

    window.onload = function () {
        aplayerbtnInit();
    }
</script>
<!------------ 加载全局背景图功能👆（开始时没有写好，这功能有点鸡肋/(ㄒoㄒ)/~~） -->




<script src="https://hzqyihui.github.io/media/js/index.js"></script>


<script>
    //加载后的事件同步（阅读量、移动端评论等
    window.onload = function () {
    //代码块高亮渲染
    document.querySelectorAll('pre code').forEach((block) => {
            hljs.highlightBlock(block);
    });
        getHotnum();
        var sharepc = $(".share").eq(0).html();
        $(".share").eq(1).html(sharepc);   //插件只会寻找一个分享标签渲染，所以需要手动添加到移动端
        aplayerbtnInit();
        $(".hljs").css({
            "padding": "20px"
        })
    }
</script>

<!-- <script src="https://hzqyihui.github.io/media/js/av-min.js"></script> -->
<!-- <script src="https://cdn.jsdelivr.net/npm/leancloud-storage@3.13.0/dist/av-min.js"></script> -->
<!-- <script src="https://cdn.jsdelivr.net/npm/leancloud-storage@4.6.1/dist/av-live-query-min.js"></script> -->